package cn.omisheep.commons.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

/**
 * @author zhouxinchen[1269670415@qq.com]
 * @since 1.0.0
 */
@SuppressWarnings("all")
public class TimeUtils {

    private TimeUtils() {
        throw new UnsupportedOperationException();
    }

    private static final String blank = " ";
    private static final String empty = "";
    private static final String zero  = "0";

    /**
     * 解析时间字符串（总和）
     *
     * @param timeVals 时间字符串
     * @return 解析的时间总和（毫秒）
     */
    public static long parseTimeValueTotal(String... timeVals) {
        long c = 0;
        for (String timeVal : timeVals) {
            c += parseTimeValue(timeVal);
        }
        return c;
    }

    /**
     * 解析时间字符串
     *
     * @param timeVal 时间字符串
     * @return 解析的时间（毫秒）
     */
    public static long parseTimeValue(String timeVal) {
        return parseTimeValue(timeVal, blank);
    }

    /**
     * 解析时间字符串
     *
     * @param timeVal 时间字符串
     * @return 解析的时间 （秒）
     */
    public static long parseTimeValueToSecond(String timeVal) {
        return TimeUnit.MILLISECONDS.toSeconds(parseTimeValue(timeVal, blank));
    }


    /**
     * 解析时间字符串
     *
     * @param timeVal   时间字符串
     * @param delimiter 分隔符
     * @return 解析的时间（秒）
     */
    public static long parseTimeValue(String timeVal,
                                      String delimiter) {
        if (timeVal == null || timeVal.equals(empty) || timeVal.equals(zero)) return 0L;
        String[] s    = timeVal.trim().split(delimiter);
        long     time = 0;
        for (String val : s) {
            time += parseTime(val);
        }
        return time;
    }

    /**
     * 反向解析，将时间解析成时间字符串
     *
     * @param ms 毫秒时间
     * @return 时间字符串
     */
    public static String parseTime(long ms) {
        return parseTime(ms, blank);
    }

    /**
     * 反向解析，将时间解析成时间字符串
     *
     * @param ms        毫秒时间
     * @param delimiter 分隔符
     * @return 时间字符串
     */
    public static String parseTime(long ms,
                                   String delimiter) {
        String _ms = ms % 1000 + TimeUnitShortName.MILLISECONDS.name;
        if (ms < 1000) return _ms;

        long   s  = ms / 1_000;
        String _s = s % 60 + TimeUnitShortName.SECONDS.name + delimiter + _ms;
        if (s < 60) return _s;

        long   m  = s / 60;
        String _m = m % 60 + TimeUnitShortName.MINUTES.name + delimiter + _s;
        if (m < 60) return _m;

        long   h  = m / 60;
        String _h = h % 24 + TimeUnitShortName.HOURS.name + delimiter + _m;
        if (h < 24) return _h;

        long d = h / 24;
        return d + TimeUnitShortName.DAYS.name + delimiter + _h;
    }


    /**
     * 解析字符串
     *
     * @param timeVal 待解析字符串
     * @return 解析之后的值，毫秒
     */
    private static long parseTime(String timeVal) {
        String normalized = timeVal.toLowerCase(Locale.ROOT).trim();

        if (normalized.endsWith(TimeUnitShortName.DAYS.name)) {
            return TimeUnit.DAYS.toMillis(trim(normalized, TimeUnitShortName.DAYS.name));
        } else if (normalized.endsWith(TimeUnitShortName.HOURS.name)) {
            return TimeUnit.HOURS.toMillis(trim(normalized, TimeUnitShortName.HOURS.name));
        } else if (normalized.endsWith(TimeUnitShortName.MINUTES.name)) {
            return TimeUnit.MINUTES.toMillis(trim(normalized, TimeUnitShortName.MINUTES.name));
        } else if (normalized.endsWith(TimeUnitShortName.MILLISECONDS.name)) {
            return TimeUnit.MILLISECONDS.toMillis(trim(normalized, TimeUnitShortName.MILLISECONDS.name));
        } else if (normalized.endsWith(TimeUnitShortName.SECONDS.name)) {
            return TimeUnit.SECONDS.toMillis(trim(normalized, TimeUnitShortName.SECONDS.name));
        } else {
            // 若没有单位，默认为毫秒
            return TimeUnit.MILLISECONDS.toMillis(
                    trim(normalized + TimeUnitShortName.MILLISECONDS.name, TimeUnitShortName.MILLISECONDS.name));
        }
    }

    /**
     * @param normalized 待裁剪字符串
     * @param suffix     后缀
     * @return 解析后的值（long）
     */
    private static long trim(String normalized,
                             String suffix) {
        String s = normalized.substring(0, normalized.length() - suffix.length()).trim();
        try {
            long value = Long.parseLong(s);
            if (value >= 0L) {
                return value;
            }
        } catch (Exception e) {
            return 0;
        }
        return 0;
    }

    /**
     * 当前时间
     *
     * @return (long) 当前时间
     */
    public static long nowTime() {
        return now().getTime();
    }

    /**
     * 当前时间
     *
     * @return {@link Date} 当前时间
     */
    public static Date now() {
        return Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 指定时间戳获得当前系统对应的时间
     *
     * @return {@link Date}
     */
    public static Date date(long date) {
        return Date.from(new Date(date).toInstant().atZone(ZoneId.systemDefault()).toInstant());
    }


    /**
     * 当前时间
     *
     * @return {@link LocalDateTime} 当前时间
     */
    public static LocalDateTime nowDateTime() {
        return LocalDateTime.now();
    }

    /**
     * 格式化的当前时间
     *
     * @return 格式化的当前时间
     */
    public static String nowString() {
        return format(now());
    }

    /**
     * 格式化的当前时间
     *
     * @param pattern 格式
     * @return 格式化的当前时间
     */
    public static String nowString(String pattern) {
        return format(pattern, now());
    }

    /**
     * 计算当前与另一个时间的差
     *
     * @param other 另一个时间
     * @return 差值（毫秒）
     */
    public static long diff(long other) {
        return nowTime() - other;
    }

    /**
     * 选定时间加上
     *
     * @param date        选定的时间
     * @param plusTimeVal 加上的时间
     * @return 时间
     */
    public static Date datePlus(Date date,
                                String plusTimeVal) {
        return datePlus(date, parseTimeValue(plusTimeVal));
    }

    /**
     * 选定时间加上
     *
     * @param date     选定的时间
     * @param plusTime 加上的时间
     * @return 时间
     */
    public static Date datePlus(Date date,
                                long plusTime) {
        return Date.from(date.toInstant().plus(plusTime, ChronoUnit.MILLIS).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 当前时间加上
     *
     * @param plusTimeVal 加上的时间
     * @return 时间
     */
    public static Date plus(String plusTimeVal) {
        return plus(parseTimeValue(plusTimeVal));
    }

    /**
     * 当前时间加上
     *
     * @param plusTime 加上的时间
     * @return 时间
     */
    public static Date plus(long plusTime) {
        return Date.from(nowPlus(plusTime).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 选定时间减去
     *
     * @param date         选定时间
     * @param minusTimeVal 减去的值
     * @return 时间
     */
    public static Date dateMinus(Date date,
                                 String minusTimeVal) {
        return dateMinus(date, parseTimeValue(minusTimeVal));
    }

    /**
     * 选定时间减去
     *
     * @param date      选定时间
     * @param minusTime 减去的值
     * @return 时间
     */
    public static Date dateMinus(Date date,
                                 long minusTime) {
        return Date.from(
                date.toInstant().minus(minusTime, ChronoUnit.MILLIS).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 当前时间减去
     *
     * @param minusTime 减去的值
     * @return 时间
     */
    public static Date minus(long minusTime) {
        return Date.from(nowMinus(minusTime).atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 当前时间减去
     *
     * @param minusTimeVal 减去的值
     * @return 时间
     */
    public static Date minus(String minusTimeVal) {
        return minus(parseTimeValue(minusTimeVal));
    }

    private static final String yyyyMMddhhmmss_pattern = "yyyy-MM-dd HH:mm:ss";
    private static final String yyyyMMdd_pattern       = "yyyy-MM-dd";
    private static final String yyyyMM_pattern         = "yyyy-MM";
    private static final String one_day                = "1d";

    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(yyyyMMddhhmmss_pattern);

    static {
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
    }

    /**
     * 日期格式化解析
     *
     * @param date 时间
     * @return 时间字符串
     */
    public static Date formatParse(String date) throws ParseException {
        return simpleDateFormat.parse(date);
    }

    /**
     * 日期格式化
     *
     * @param date 时间
     * @return 时间字符串
     */
    public static String format(Date date) {
        return simpleDateFormat.format(date);
    }

    /**
     * 日期格式化
     *
     * @param pattern 格式化
     * @param date    时间
     * @return 时间格式化
     */
    public static String format(String pattern,
                                Date date) {
        return new SimpleDateFormat(pattern).format(date);
    }

    /**
     * 某年在某月的最大天数
     *
     * @param year  年
     * @param month 月
     * @return 选定月的最大天数
     */
    public static int maxDaysInMonth(int year,
                                     int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        return cal.getActualMaximum(Calendar.DATE);
    }

    /**
     * 现在的年[0]和月[1]
     *
     * @return 昨天的年和月
     */
    public static int[] currentYearAndMonth() {
        LocalDateTime now = LocalDateTime.now();
        return new int[]{now.getYear(), now.getMonthValue()};
    }

    /**
     * 昨天的年[0]和月[1]
     *
     * @return 昨天的年和月
     */
    public static int[] yesterdayYearAndMonth() {
        LocalDateTime localDateTime = nowMinus(one_day);
        return new int[]{localDateTime.getYear(), localDateTime.getMonthValue()};
    }

    /**
     * 当前时间(年)
     *
     * @return 当前时间(年)
     */
    public static int currentYear() {
        return LocalDateTime.now().getYear();
    }

    /**
     * 当前时间(月)
     *
     * @return 当前时间(月)
     */
    public static int currentMonth() {
        return LocalDateTime.now().getMonthValue();
    }

    /**
     * 当前时间(天)
     *
     * @return 当前时间(天)
     */
    public static int currentDay() {
        return LocalDateTime.now().getDayOfMonth();
    }

    /**
     * 当前时间(小时)
     *
     * @return 当前时间(小时)
     */
    public static int currentHour() {
        return LocalDateTime.now().getHour();
    }

    /**
     * 当前时间(分钟)
     *
     * @return 当前时间(分钟)
     */
    public static int currentMinute() {
        return LocalDateTime.now().getMinute();
    }

    /**
     * 当前时间加上
     *
     * @param plusTime 加上的值
     * @return 前时间加上的值
     */
    public static LocalDateTime nowPlus(long plusTime) {
        return LocalDateTime.now().plus(plusTime, ChronoUnit.MILLIS);
    }

    /**
     * 当前时间加上
     *
     * @param plusTimeVal 加上的值
     * @return 前时间加上的值
     */
    public static LocalDateTime nowPlus(String plusTimeVal) {
        return LocalDateTime.now().plus(parseTimeValue(plusTimeVal), ChronoUnit.MILLIS);
    }

    /**
     * 当前时间减去
     *
     * @param minusTime 减去的值
     * @return 前时间减去的值
     */
    public static LocalDateTime nowMinus(long minusTime) {
        return LocalDateTime.now().minus(minusTime, ChronoUnit.MILLIS);
    }

    /**
     * 当前时间减去
     *
     * @param minusTimeVal 减去的值
     * @return 前时间减去的值
     */
    public static LocalDateTime nowMinus(String minusTimeVal) {
        return LocalDateTime.now().minus(parseTimeValue(minusTimeVal), ChronoUnit.MILLIS);
    }

    /**
     * 下一分钟（整分）
     *
     * @return Date
     */
    public static Date nextIntactDateForMinute() {
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.SECOND, 0);
        ca.set(Calendar.MILLISECOND, 0);
        ca.add(Calendar.MINUTE, 1);
        return ca.getTime();
    }

    /**
     * 明天（整点）
     *
     * @return Date
     */
    public static Date nextIntactDateForDay() {
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.HOUR, 0);
        ca.set(Calendar.MINUTE, 0);
        ca.set(Calendar.SECOND, 0);
        ca.set(Calendar.MILLISECOND, 0);
        ca.add(Calendar.DAY_OF_MONTH, 1);
        return ca.getTime();
    }

    /**
     * 判断选择的时间是否是本周
     *
     * @param date 选择的时间
     * @return 是否是本周
     */
    public static boolean isThisWeek(Date date) {
        Calendar calendar    = Calendar.getInstance();
        int      currentWeek = calendar.get(Calendar.WEEK_OF_YEAR);
        calendar.setTime(date);
        int chooseWeek = calendar.get(Calendar.WEEK_OF_YEAR);
        return chooseWeek == currentWeek;
    }

    /**
     * 判断选择的时间是否是今天
     *
     * @param date 选择的时间
     * @return 是否是今天
     */
    public static boolean isToday(Date date) {
        return isThisTime(date, yyyyMMdd_pattern);
    }

    /**
     * 判断选择的时间是否是本月
     *
     * @param date 选择的时间
     * @return 是否是本月
     */
    public static boolean isThisMonth(Date date) {
        return isThisTime(date, yyyyMM_pattern);
    }

    private static boolean isThisTime(Date date,
                                      String pattern) {
        return format(pattern, date).equals(format(pattern, new Date()));
    }

    private enum TimeUnitShortName {
        NANOSECONDS(TimeUnit.NANOSECONDS, "ns"), MICROSECONDS(TimeUnit.MICROSECONDS, "μs"), MILLISECONDS(
                TimeUnit.MILLISECONDS, "ms"), SECONDS(TimeUnit.SECONDS, "s"), MINUTES(TimeUnit.MINUTES, "m"), HOURS(
                TimeUnit.HOURS, "h"), DAYS(TimeUnit.DAYS, "d");

        TimeUnitShortName(TimeUnit unit,
                          String name) {
            this.unit = unit;
            this.name = name;
        }

        private final TimeUnit unit;
        private final String   name;
    }

    public static String timeShortName(TimeUnit unit) {
        return TimeUnitShortName.valueOf(unit.name()).name;
    }

}
